%%%-------------------------------------------------------------------
%%% @author mirahs(2892727493@qq.com)
%%% @doc
%%%     数据版本处理
%%% @end
%%%-------------------------------------------------------------------
-module(parse).

-export([
    parse/2
    ,parse_list/2

    ,test/0
]).

-include("common.hrl").
-include("parse.hrl").


%%%===================================================================
%%% API functions
%%%===================================================================

%% @spec parse(Parser, Data) -> {ok, NewData} | {false, Reason}
%% Parser = atom | #parse{} 
%% Data = NewData = tuple()
%% Reason = term()
%% 解析并转换数据版本
parse(_Parser, undefined) ->
    {ok, undefined};
parse({M, F, A}, Data) ->
    case erlang:apply(M, F, [Data | A]) of
        {ok, NewData} ->
            {ok, NewData};
        {false, Reason} ->
            {false, Reason}
    end;
parse(Parser, Data) ->
    Index = get_ver_index(Parser),
    Ver = erlang:element(Index, Data),
    case is_integer(Ver) of
        true ->
            case loop_parse(Parser, Ver, Data) of
                {ok, NewData2} ->
                    case validate(Parser, NewData2) of
                        true ->
                            case do_parse_field(get_field_parse(Parser), NewData2) of
                                {ok, NewData3} ->
                                    case process_after(Parser, NewData3) of
                                        {ok, NewData4} ->
                                            {ok, NewData4};
                                        {false, Reason} ->
                                            {false, Reason}
                                    end;
                                {false, Reason} ->
                                    {false, Reason}
                            end;
                        false ->
                            ?ERR("数据validate出错: parser=~w, data=~w", [Parser, NewData2]),
                            {false, <<"数据validate出错">>}
                    end;
                {false, Reason} ->
                    ?ERR("解析数据出错: parser=~w, data=~w, reason=~w", [Parser, Data, Reason]),
                    {false, Reason}
            end;
        false ->
            ?ERR("不正确的版本号: parser=~w, data=~w", [Parser, Data]),
            {false, <<"不正确的版本号">>}
    end.

%% @spec parse_list(Parser, L) -> {ok, NewData} | {false, Reason}
%% Parser = atom | #parse{}
%% L = NewData = list()
%% Reason = term()
%% 解析列表中的数据
parse_list(Parser, L) when is_list(L) ->
    parse_list(Parser, L, []).
parse_list(_Parser, [], Back) -> {ok, lists:reverse(Back)};
parse_list(Parser, [H | T], Back) ->
    case parse(Parser, H) of
        {ok, NewH} ->
            parse_list(Parser, T, [NewH | Back]);
        {false, Reason} ->
            ?ERR("解析数据出错: parser=~w, data=~w", [Parser, H]),
            {false, Reason}
    end.


%% 版本转换loop
loop_parse(Parser, Ver, Data) ->
    case get_ver(Parser, Ver) of
        ok ->
            {ok, Data};
        _ ->
            case do_loop_parse(Parser, Ver, tuple_to_list(Data)) of
                {ok, NewData, NewVer} ->
                    {ok, erlang:setelement(get_ver_index(Parser), list_to_tuple(NewData), NewVer)};
                %{ok, NewData};
                {false, Reason} ->
                    {false, Reason}
            end
    end.

do_loop_parse(Parser, Ver, Data) ->
    case get_ver(Parser, Ver) of
        ok ->
            {ok, Data, Ver};
        ParseVer = #parse_ver{} ->
            case handle_parse_ver(Ver, ParseVer, Data) of
                {ok, NewData} ->
                    do_loop_parse(Parser, Ver + 1, NewData);
                {false, Reason} ->
                    ?ERR("解析数据出错: parser=~w, ver=~w, parse_ver=~w", [Parser, Ver, ParseVer]),
                    {false, Reason}
            end;
        _Else ->
            {false, <<"错误的do_ver">>}
    end.

%% 版本转换
handle_parse_ver(_, #parse_ver{do = {add, Field}}, Data) ->
    {ok, Data ++ [Field]};
handle_parse_ver(_, #parse_ver{do = {add_list, Field}}, Data) ->
    {ok, Data ++ Field};
handle_parse_ver(_, #parse_ver{do = nothing}, Data) ->
    {ok, Data};
handle_parse_ver(Ver, #parse_ver{do = Fun}, Data) when is_function(Fun) ->
    case Fun(Ver, Data) of
        {ok, NewData} ->
            {ok, NewData};
        {false, Reason} ->
            {false, Reason}
    end;
handle_parse_ver(_Ver, _ParseVer, _Data) ->
    {false, <<"错误的parse_ver">>}.

%% 处理子数据
do_parse_field([], Data) ->
    {ok, Data};
do_parse_field([{Index, Parser} | T], Data) ->
    Field = erlang:element(Index, Data),
    case parse(Parser, Field) of
        {ok, NewField} ->
            NewData = erlang:setelement(Index, Data, NewField),
            do_parse_field(T, NewData);
        {false, Reason} ->
            ?ERR("解析数据出错: parser=~w, data=~w", [Parser, Field]),
            {false, Reason}
    end;
do_parse_field([{Index, Parser, list} | T], Data) ->
    Field = erlang:element(Index, Data),
    case parse_list(Parser, Field) of
        {ok, NewField} ->
            NewData = erlang:setelement(Index, Data, NewField),
            do_parse_field(T, NewData);
        {false, Reason} ->
            ?ERR("解析数据出错: parser=~w, data=~w", [Parser, Field]),
            {false, Reason}
    end.

%% 获取版本号索引
get_ver_index(Parser) when is_atom(Parser) ->
    Parser:get_ver_index();
get_ver_index(#parse{get_ver_index = F}) ->
    F().

%% 获取版本信息
get_ver(Parser, Ver) when is_atom(Parser) ->
    Parser:get_ver(Ver);
get_ver(#parse{get_ver = F}, Ver) ->
    F(Ver).

%% 验证数据
validate(Parser, Data) when is_atom(Parser) ->
    Parser:validate(Data);
validate(#parse{validate = F}, Data) ->
    F(Data).

%% 获取子属性parse
get_field_parse(Parser) when is_atom(Parser) ->
    Parser:get_field_parse();
get_field_parse(#parse{get_field_parse = F}) ->
    F().

%% 解析后的处理
process_after(Parser, Data) when is_atom(Parser) ->
    Parser:process_after(Data);
process_after(#parse{process_after = F}, Data) ->
    F(Data).

%%---------------------------------------------------------------------
%% 测试
%%---------------------------------------------------------------------

test() ->
    test_1(),
    test_2(),
    test_3(),
    test_4(),
    ok.

%% 增加一个字段
test_1() ->
    Data = {test_rec, 1, 2, [], <<"abc">>},
    TarData = {test_rec, 2, 2, [], <<"abc">>, []},
    Parse = #parse{
        get_ver_index = fun() -> 2 end
        ,validate = fun(D) -> D =:= TarData end
        ,process_after = fun(D) -> {ok, D} end
        ,get_field_parse = fun() -> [] end
        ,get_ver = fun(1) -> #parse_ver{ver = 1, do = {add, []}};
            (2) -> ok
                   end
    },
    NewData = parse:parse(Parse, Data),
    NewData.

%% 使用fun修改特定字段
test_2() ->
    Data = {test_rec, 1, 2, [], <<"abc">>},
    TarData = {test_rec, 2, 3, [1], <<"bcd">>},
    Parse = #parse{
        get_ver_index = fun() -> 2 end
        ,validate = fun(D) -> D =:= TarData end
        ,process_after = fun(D) -> {ok, D} end
        ,get_field_parse = fun() -> [] end
        ,get_ver = fun(1) -> #parse_ver{ver = 1, do = fun(1, [test_rec, Ver, Number, List, _String]) -> {ok, [test_rec, Ver, Number + 1, [1 | List], <<"bcd">>]} end};
            (2) -> ok
                   end
    },
    NewData = parse:parse(Parse, Data),
    NewData.

%% 使用fun修改特定字段， 增加一个字段
test_3() ->
    Data = {test_rec, 1, 2, [], <<"abc">>},
    TarData = {test_rec, 3, 3, [1], <<"bcd">>, []},
    Parse = #parse{
        get_ver_index = fun() -> 2 end
        ,validate = fun(D) -> D =:= TarData end
        ,process_after = fun(D) -> {ok, D} end
        ,get_field_parse = fun() -> [] end
        ,get_ver = fun(1) -> #parse_ver{ver = 1, do = fun(1, [test_rec, Ver, Number, List, _String]) -> {ok, [test_rec, Ver, Number + 1, [1 | List], <<"bcd">>]} end};
            (2) -> #parse_ver{ver = 2, do = {add, []}};
            (3) -> ok
                   end
    },
    NewData = parse:parse(Parse, Data),
    NewData.

%% 增加一个字段, 使用fun修改特定字段
test_4() ->
    Data = {test_rec, 1, 2, [], <<"abc">>},
    TarData = {test_rec, 3, 3, [1], <<"bcd">>, []},
    Parse = #parse{
        get_ver_index = fun() -> 2 end
        ,validate = fun(D) -> D =:= TarData end
        ,process_after = fun(D) -> {ok, D} end
        ,get_field_parse = fun() -> [] end
        ,get_ver = fun
                       (1) -> #parse_ver{ver = 1, do = {add, []}};
                       (2) -> #parse_ver{ver = 2, do = fun(2, [test_rec, Ver, Number, List, _String, NewList]) -> {ok, [test_rec, Ver, Number + 1, [1 | List], <<"bcd">>, NewList]} end};
                       (3) -> ok
                   end
    },
    NewData = parse:parse(Parse, Data),
    NewData.
